﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Xml.Linq;
using System.Xml;
using System.Xml.XPath;
using System.Data.Linq;
using Infrastructure.Aspect.Extensions;

namespace Infrastructure.WpfClient.Accessibility
{
    public class AccessibilityService : Infrastructure.WpfClient.Accessibility.IAccessibilityService
    {
        [Dependency("ForeignExchangeAspect")]
        public Database InfrastructureDatabase { protected get; set; }


        private XElement AccessibilityContext { get; set; }

        private void WriteEntity(string application, Guid userid, string Setting, DateTime modified)
        {
            var cmd = InfrastructureDatabase.GetStoredProcCommand("usp_Accessibility_Insert");
            InfrastructureDatabase.AddInParameter(cmd, "ApplicationName", System.Data.DbType.String, application);
            InfrastructureDatabase.AddInParameter(cmd, "UserId", System.Data.DbType.Guid, userid);
            InfrastructureDatabase.AddInParameter(cmd, "Setting", System.Data.DbType.Xml, Setting);
            InfrastructureDatabase.AddInParameter(cmd, "Modified", System.Data.DbType.DateTime, modified);
            this.InfrastructureDatabase.ExecuteNonQuery(cmd);
            AccessibilityContext = null;
        }
        private XElement internalReadSetting(string application, Guid userId)
        {
            if (this.AccessibilityContext != null)
            {
                return this.AccessibilityContext;
                
            }
            else
            {
                System.Data.Common.DbCommand cmd = null;
                cmd = InfrastructureDatabase.GetSqlStringCommand("select * from Accessibility where ApplicationName=@ApplicationName and UserId=@UserId");
                InfrastructureDatabase.AddInParameter(cmd, "ApplicationName", System.Data.DbType.String, application);
                InfrastructureDatabase.AddInParameter(cmd, "UserId", System.Data.DbType.Guid, userId);
                var reader = this.InfrastructureDatabase.ExecuteReader(cmd);
                if (reader.Read())
                {
                    this.AccessibilityContext = XElement.Parse(reader["Setting"] as string);
                    return this.AccessibilityContext;
                }
                else
                    return null;
            }

        }

        private void BuildTypeAndValue<T>(T target, out string type, out string value)
        {
            type = "na";
            value = target.ToString();
            if (target is string)
            {
                type = "String";
                value = target.ToString();
            }
            if (target is int)
            {
                type = "Int";
                value = target.ToString();
            }
            if (target is decimal)
            {
                type = "Decimal";
                value = target.ToString();
            }
            if (target is double)
            {
                type = "Double";
                value = target.ToString();
            }
        }
        private T BuildFromElement<T>(XElement target)
        {
            T response = default(T);
            if (target.Attribute("type").Value == "String")
            {
                response = (T)(Object)target.Attribute("value").Value;
            }
            if (target.Attribute("type").Value == "Int")
            {
                response = (T)(Object)int.Parse(target.Attribute("value").Value);
            }
            if (target.Attribute("type").Value == "Decimal")
            {
                response = (T)(Object)decimal.Parse(target.Attribute("value").Value);
            }
            if (target.Attribute("type").Value == "Double")
            {
                response = (T)(Object)double.Parse(target.Attribute("value").Value);
            }
            return response;
        }


        private T internalGetSetting<T>(string application, Guid userId, Queue<string> codes, T defaultValue)
        {
            string type = "na";
            string value = "";
            BuildTypeAndValue<T>(defaultValue, out type, out value);
            var tree = this.internalReadSetting(application, userId);
            bool modified = false;
            XElement result = null;
            if (tree == null)
            {
                tree = new XElement("Settings");
                result = this.internalGetNavigation(codes.Clone(), type, value, tree, ref modified);
            }
            else
            {
                result = this.internalGetNavigation(codes.Clone(), type, value, tree, ref modified);
            }
            if (modified)
                this.WriteEntity(application, userId, tree.ToString(), DateTime.Now);
            return BuildFromElement<T>(result);
        }
        private void internalSetSetting<T>(string application, Guid userId, Queue<string> codes, T defaultValue)
        {
            string type = "na";
            string value = "";
            BuildTypeAndValue<T>(defaultValue, out type, out value);
            var tree = this.internalReadSetting(application, userId);
            bool modified = false;
            XElement result = null;
            if (tree == null)
            {
                tree = new XElement("Settings");
                result = this.internalSetNavigation(codes.Clone(), type, value, tree, ref modified);
            }
            else
            {
                result = this.internalSetNavigation(codes.Clone(), type, value, tree, ref modified);
            }
            if (modified)
                this.WriteEntity(application, userId, tree.ToString(), DateTime.Now);
        }
        public  T getSetting<T>(string application, Guid userId, string code, T defaultValue)
        {
            var codes = new Queue<string>();
            codes.Enqueue(code);
            return internalGetSetting<T>(application, userId, codes, defaultValue);
        }
        public  T getSetting<T>(string application, Guid userId, string code, string codeone, T defaultValue)
        { 
            var codes = new Queue<string>();
            codes.Enqueue(code);
            codes.Enqueue(codeone);
            return internalGetSetting<T>(application, userId, codes, defaultValue);
        }
        public  T getSetting<T>(string application, Guid userId, string code, string codeone, string codetwo, T defaultValue)
        {
            var codes = new Queue<string>();
            codes.Enqueue(code);
            codes.Enqueue(codeone);
            codes.Enqueue(codetwo);
            return internalGetSetting<T>(application, userId, codes, defaultValue);
        }
        public  void setSetting<T>(string application, Guid userId, string code, T defaultValue)
        {
            var codes = new Queue<string>();
            codes.Enqueue(code);
            internalSetSetting<T>(application, userId, codes, defaultValue);
        }
        public  void setSetting<T>(string application, Guid userId, string code, string codeone, T defaultValue)
        {
            var codes = new Queue<string>();
            codes.Enqueue(code);
            codes.Enqueue(codeone);
            internalSetSetting<T>(application, userId, codes, defaultValue);
        }
        public  void setSetting<T>(string application, Guid userId, string code, string codeone, string codetwo, T defaultValue)
        {
            var codes = new Queue<string>();
            codes.Enqueue(code);
            codes.Enqueue(codeone);
            codes.Enqueue(codetwo);
            internalSetSetting<T>(application, userId, codes, defaultValue);
        }

        private XElement internalSetNavigation(Queue<string> codes, string type, string value, XElement tree, ref bool modified)
        {
            var code = codes.Dequeue();
            XElement ent = (from e in tree.Descendants("Setting") where e.Attribute("name").Value == code select e).FirstOrDefault();

            if (codes.Count == 0)
            {
                if (ent == null)
                {
                    ent = new XElement("Setting",
                                new XAttribute("name", code),
                                new XAttribute("type", type),
                                new XAttribute("value", value)
                                );
                    tree.Add(ent);
                    modified = true;
                }
                else
                {
                    if (ent.Attribute("value").Value != value)
                    {
                        ent.Attribute("type").Value = type;
                        ent.Attribute("value").Value = value;
                        modified = true;
                    }
                }
                return ent;
            }
            else
            {
                if (ent == null)
                {
                    ent = new XElement("Setting",
                                new XAttribute("name", code),
                                new XAttribute("type", "na"),
                                new XAttribute("value", "")
                                );
                    tree.Add(ent);
                    modified = true;
                }

                return internalSetNavigation(codes, type, value, ent, ref modified);
            }
        }
        private XElement internalGetNavigation(Queue<string> codes, string type, string value, XElement tree, ref bool modified)
        {
            var code = codes.Dequeue();
            XElement ent = (from e in tree.Descendants("Setting") where e.Attribute("name").Value == code select e).FirstOrDefault();



            if (codes.Count == 0)
            {
                if (ent == null)
                {
                    ent = new XElement("Setting",
                                new XAttribute("name", code),
                                new XAttribute("type", type),
                                new XAttribute("value", value)
                                );
                    tree.Add(ent);
                    modified = true;
                }
                return ent;
            }
            else
            {
                if (ent == null)
                {
                    ent = new XElement("Setting",
                                new XAttribute("name", code),
                                new XAttribute("type", "na"),
                                new XAttribute("value", "")
                                );
                    tree.Add(ent);
                    modified = true;
                }

                return internalGetNavigation(codes, type, value, ent, ref modified);
            }
        }
    }
}
